import torch
import numpy as np
import matplotlib.pyplot as plt📘 Note Format Guide
This format serves as a structured guide for organizing lecture content, personal interpretation, experiments, and study-related questions.
| Type | What It Means | When I Use It |
|---|---|---|
| 📝 Lecture | Original material from the professor’s notes | When I’m referencing core concepts or provided code |
| 🗣️ In-Class Note | Verbal explanations shared during the lecture | When I want to record something the professor said in class but didn’t include in the official notes |
| ✍️ My Note | My thoughts, interpretations, or additional explanations | When I reflect on or explain something in my own words |
| 🔬 Experiment | Code I tried out or changed to explore further | When I test variations or go beyond the original example |
| ❓ Question | Questions I had while studying | When I want to revisit or research something more deeply |
📝 🗣️ ✍️ 🔬 ❓
1. 강의노트 원본 및 영상 링크
2. Imports 📝
plt.rcParams['figure.figsize'] = (4.5, 3.0)3. 선택학습: 데이터시각화 📝
- 데이터시각화2023: https://guebin.github.io/DV2023/
- 01wk-2: https://guebin.github.io/DV2023/posts/01wk-2.html
- 02wk-1: https://guebin.github.io/DV2023/posts/02wk-1.html
🗣️ 시각화 코드를 이해하고 싶으면 학습 추천
4. 파라메터의 학습과정 음미 📝
torch.manual_seed(43052)
x,_ = torch.randn(100).sort()
eps = torch.randn(100)*0.5
X = torch.stack([torch.ones(100),x],axis=1)
W = torch.tensor([[2.5],[4.0]])
y = X@W + eps.reshape(100,1)
x = X[:,[1]]# 지난시간복습
# 지도학습문제: (x,y) 관찰하고 x -> y 인 패턴을 찾는 문제 (지도학습문제, supervised learning)
# 모델링: (x,y) 관찰을 해보니까 scatter plot이 직선의 모양을 하고 있음.. --> 패턴이 직선이겠네? --> y=X@W+ϵ
## - 모델: y=X@W+ϵ // y 물결 X@W
# 학습: 결국 회귀분석문제는 What을 찾는 문제 // 파라메터 What의 값을 update하는 과정을 학습
# 학습의방법?
# 1. yhat을 구한다. <-- 모델링 (도메인지식이 정통한 어떤 사람)
# 2. loss를 계산한다. <-- 손실함수를 설계 (통계)
# 3. loss미분한다. <-- 미분을 계산하는 torch와 같은 프로그램이 있어야함 (컴퓨터공학과)
# 4. update한다. <-- 업데이트공식이 있었음. 이걸 알아야함. (산공)🗣️(
- 모델 (직선)
- 통계: \(y=X@W + \epsilon\)
- 비통계: \(y \approx X@W\)
- 학습 (결과는 동일하나 느낌이 약간 다름)
- 통계: parameter를 estimation (전체 공간에서 점으로 하나를 찍음, 신뢰구간으로 보완)
- 비통계: parameter를 learning[일단 하나 선택하고 update] (신뢰구간 불가능-오차항이 없어서)
What = torch.tensor([[-5.0], [10.0]], requires_grad=True)
yhat = X@What # step1
loss = torch.sum((yhat-y)**2) # step2
loss.backward() # step3
What.data = What.data - 0.001*What.grad # step4
What.grad = None- torch의 장점
- 미분 가능 (미분 안 할거면 numpy 쓰면 됨)
- GPU 사용 가능
What.datatensor([[-3.6577],
[ 8.8111]])
- 업데이트된 결과를 가지고 step1부터 다시 반복
yhat = X@What # step1
loss = torch.sum((yhat-y)**2) # step2
loss.backward() # step3
What.data = What.data - 0.001*What.grad # step4
What.grad = NoneWhat.datatensor([[2.4075],
[4.0276]])
- 위 두 코드를 반복하다보면 어느 순간 결과가 빨리 변하지 않는 느낌
- 10번 넘게 반복한 결과
- True 값이 이 근처
plt.plot(x,y,'o') # observed data
plt.plot(x,yhat.data,'--') # 추정값
- 중간 과정을 아래와 같이 살펴보려고 함
- 결과를 보기 쉽게 하려고 벡터화: .reshpae(-1)
- 두 번째 코드는 업데이트 계산 과정을 보기 쉽게하려고 주석 처리 및 부호 수정
What = torch.tensor([[-5.0], [10.0]], requires_grad=True)yhat = X@What
loss = torch.sum((yhat-y)**2)
print(loss)
loss.backward()
print(What.data.reshape(-1)) # 수정전
print(What.grad.reshape(-1)) # 기울기값
print(What.grad.reshape(-1)*1/1000) # 기울기값*학습률
What.data = What.data - 0.001*What.grad
print(What.data.reshape(-1)) # 수정후
What.grad = Nonetensor(8587.6875, grad_fn=<SumBackward0>)
tensor([-5., 10.])
tensor([-1342.2524, 1188.9305])
tensor([-1.3423, 1.1889])
tensor([-3.6577, 8.8111])
What = torch.tensor([[-5.0], [10.0]], requires_grad=True)yhat = X@What
loss = torch.sum((yhat-y)**2)
print(loss)
loss.backward()
print(What.data.reshape(-1)) # 수정전
#print(What.grad.reshape(-1)) # 기울기값
print(-What.grad.reshape(-1)*1/1000) # 기울기값*학습률
What.data = What.data - 0.001*What.grad
print(What.data.reshape(-1)) # 수정후
What.grad = Nonetensor(8587.6875, grad_fn=<SumBackward0>)
tensor([-5., 10.])
tensor([ 1.3423, -1.1889])
tensor([-3.6577, 8.8111])
- 반복하며 결과 비교 ✍️
tensor(8587.6875, grad_fn=<SumBackward0>)
tensor([-5., 10.])
tensor([ 1.3423, -1.1889])
tensor([-3.6577, 8.8111])
tensor(5675.2104, grad_fn=<SumBackward0>)
tensor([-3.6577, 8.8111])
tensor([ 1.1029, -0.9499])
tensor([-2.5548, 7.8612])
tensor(3755.6375, grad_fn=<SumBackward0>)
tensor([-2.5548, 7.8612])
tensor([ 0.9056, -0.7596])
tensor([-1.6492, 7.1016])
yhat = X@What
loss = torch.sum((yhat-y)**2)
print(loss)
loss.backward()
print(What.data.reshape(-1)) # 수정전
#print(What.grad.reshape(-1)) # 기울기값
print(-What.grad.reshape(-1)*1/1000) # 기울기값*학습률
What.data = What.data - 0.001*What.grad
print(What.data.reshape(-1)) # 수정후
What.grad = Nonetensor(3755.6375, grad_fn=<SumBackward0>)
tensor([-2.5548, 7.8612])
tensor([ 0.9056, -0.7596])
tensor([-1.6492, 7.1016])
)🗣️
A. print
What = torch.tensor([[-5.0],[10.0]],requires_grad=True)
alpha = 0.001
print(f"시작값 = {What.data.reshape(-1)}")
for epoc in range(30):
yhat = X @ What
loss = torch.sum((y-yhat)**2)
loss.backward()
What.data = What.data - alpha * What.grad
print(f'loss = {loss:.2f} \t 업데이트폭 = {-alpha * What.grad.reshape(-1)} \t 업데이트결과: {What.data.reshape(-1)}')
What.grad = None시작값 = tensor([-5., 10.])
loss = 8587.69 업데이트폭 = tensor([ 1.3423, -1.1889]) 업데이트결과: tensor([-3.6577, 8.8111])
loss = 5675.21 업데이트폭 = tensor([ 1.1029, -0.9499]) 업데이트결과: tensor([-2.5548, 7.8612])
loss = 3755.64 업데이트폭 = tensor([ 0.9056, -0.7596]) 업데이트결과: tensor([-1.6492, 7.1016])
loss = 2489.58 업데이트폭 = tensor([ 0.7431, -0.6081]) 업데이트결과: tensor([-0.9061, 6.4935])
loss = 1654.04 업데이트폭 = tensor([ 0.6094, -0.4872]) 업데이트결과: tensor([-0.2967, 6.0063])
loss = 1102.32 업데이트폭 = tensor([ 0.4995, -0.3907]) 업데이트결과: tensor([0.2028, 5.6156])
loss = 737.84 업데이트폭 = tensor([ 0.4091, -0.3136]) 업데이트결과: tensor([0.6119, 5.3020])
loss = 496.97 업데이트폭 = tensor([ 0.3350, -0.2519]) 업데이트결과: tensor([0.9469, 5.0501])
loss = 337.71 업데이트폭 = tensor([ 0.2742, -0.2025]) 업데이트결과: tensor([1.2211, 4.8477])
loss = 232.40 업데이트폭 = tensor([ 0.2243, -0.1629]) 업데이트결과: tensor([1.4454, 4.6848])
loss = 162.73 업데이트폭 = tensor([ 0.1834, -0.1311]) 업데이트결과: tensor([1.6288, 4.5537])
loss = 116.63 업데이트폭 = tensor([ 0.1500, -0.1056]) 업데이트결과: tensor([1.7787, 4.4480])
loss = 86.13 업데이트폭 = tensor([ 0.1226, -0.0851]) 업데이트결과: tensor([1.9013, 4.3629])
loss = 65.93 업데이트폭 = tensor([ 0.1001, -0.0687]) 업데이트결과: tensor([2.0014, 4.2942])
loss = 52.57 업데이트폭 = tensor([ 0.0818, -0.0554]) 업데이트결과: tensor([2.0832, 4.2388])
loss = 43.72 업데이트폭 = tensor([ 0.0668, -0.0447]) 업데이트결과: tensor([2.1500, 4.1941])
loss = 37.86 업데이트폭 = tensor([ 0.0545, -0.0361]) 업데이트결과: tensor([2.2045, 4.1579])
loss = 33.97 업데이트폭 = tensor([ 0.0445, -0.0292]) 업데이트결과: tensor([2.2490, 4.1287])
loss = 31.40 업데이트폭 = tensor([ 0.0363, -0.0236]) 업데이트결과: tensor([2.2853, 4.1051])
loss = 29.70 업데이트폭 = tensor([ 0.0296, -0.0191]) 업데이트결과: tensor([2.3150, 4.0860])
loss = 28.57 업데이트폭 = tensor([ 0.0242, -0.0155]) 업데이트결과: tensor([2.3392, 4.0705])
loss = 27.83 업데이트폭 = tensor([ 0.0197, -0.0125]) 업데이트결과: tensor([2.3589, 4.0580])
loss = 27.33 업데이트폭 = tensor([ 0.0161, -0.0101]) 업데이트결과: tensor([2.3750, 4.0479])
loss = 27.00 업데이트폭 = tensor([ 0.0131, -0.0082]) 업데이트결과: tensor([2.3881, 4.0396])
loss = 26.79 업데이트폭 = tensor([ 0.0107, -0.0067]) 업데이트결과: tensor([2.3988, 4.0330])
loss = 26.64 업데이트폭 = tensor([ 0.0087, -0.0054]) 업데이트결과: tensor([2.4075, 4.0276])
loss = 26.55 업데이트폭 = tensor([ 0.0071, -0.0044]) 업데이트결과: tensor([2.4146, 4.0232])
loss = 26.48 업데이트폭 = tensor([ 0.0058, -0.0035]) 업데이트결과: tensor([2.4204, 4.0197])
loss = 26.44 업데이트폭 = tensor([ 0.0047, -0.0029]) 업데이트결과: tensor([2.4251, 4.0168])
loss = 26.41 업데이트폭 = tensor([ 0.0038, -0.0023]) 업데이트결과: tensor([2.4290, 4.0144])
- 🗣️
- loss만 보면 점점 감소함, 갈수록 감소하는 폭도 작아지며 26 근처로 수렴
- 업데이트 폭도 처음에는 컸다가 감소
- 이에 따라 업데이트 결과도 갈수록 잘 안 바뀜
B. 시각화 – yhat의 관점에서!
🗣️(
What = torch.tensor([[-5.0], [10.0]], requires_grad=True)yhat = X@What
loss = torch.sum((yhat-y)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = Noneplt.plot(x,y,'o')
plt.plot(x, (X@What).data, '--', color="C1") # 선 색깔 주황색 고정
- 아래 코드를 반복하며 지켜보면 선이 변화하는 것을 볼 수 있음 (밑의 그래프는 여러번 반복한 최종 결과)
yhat = X@What
loss = torch.sum((yhat-y)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = None
plt.plot(x,y,'o')
plt.plot(x, (X@What).data, '--', color="C1")
- 한 가지 아쉬운 점: 중간 과정의 그래프가 사라짐
What = torch.tensor([[-5.0], [10.0]], requires_grad=True)plt.plot(x,y,'o')
fig = plt.gcf() # 중간 그림을 저장 (호출 가능) get current figure
ax = fig.gca() # get current axes (axes: axis의 복수형, 여기서는 x축,y축 모두를 지칭)
yhat = X@What
loss = torch.sum((yhat-y)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = None
ax.plot(x, (X@What).data, '--', color="C1") # plt를 ax로 수정fig # 1번 실행
- 1번 더 실행하면 겹쳐짐
ax = fig.gca() # get current axes (axes: axis의 복수형, 여기서는 x축,y축 모두를 지칭)
yhat = X@What
loss = torch.sum((yhat-y)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = None
ax.plot(x, (X@What).data, '--', color="C1") # plt를 ax로 수정fig # 1번 더 실행 (겹쳐짐)
- 초기화 후 반복하면 업데이트된 폭을 볼 수 있음 (점점 줄어드는 것 같음)
What = torch.tensor([[-5.0], [10.0]], requires_grad=True)plt.plot(x,y,'o')
fig = plt.gcf()
ax = fig.gca()
yhat = X@What
loss = torch.sum((yhat-y)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = None
ax.plot(x, (X@What).data, '--', color="C1")
fig
- 제목을 넣을 수도 있음 (set_title, 단순 문자열 아니여도 가능)
What = torch.tensor([[-5.0], [10.0]], requires_grad=True)
plt.plot(x,y,'o')
fig = plt.gcf()
for epoc in range(20):
ax = fig.gca()
yhat = X@What
loss = torch.sum((yhat-y)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = None
ax.plot(x, (X@What).data, '--', color="C1")
ax.set_title(What.data.reshape(-1))
figfig
)🗣️
What = torch.tensor([[-5.0],[10.0]],requires_grad=True)
alpha = 0.001
plt.plot(x,y,'o',label = "observed")
fig = plt.gcf()
ax = fig.gca()
ax.plot(x,X@What.data,'--',color="C1")
for epoc in range(30):
yhat = X @ What
loss = torch.sum((y-yhat)**2)
loss.backward()
What.data = What.data - alpha * What.grad
ax.plot(x,X@What.data,'--',color="C1",alpha=0.1)
What.grad = None
🗣️ alpha: 겹쳐지면 진해짐
C. 시각화 – loss의 관점에서!!
🗣️(
def plot_loss():
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
w0 = np.arange(-6, 11, 0.5)
w1 = np.arange(-6, 11, 0.5)
W1,W0 = np.meshgrid(w1,w0)
LOSS=W0*0
for i in range(len(w0)):
for j in range(len(w1)):
LOSS[i,j]=torch.sum((y-w0[i]-w1[j]*x)**2)
ax.plot_surface(W0, W1, LOSS, rstride=1, cstride=1, color='b',alpha=0.1)
ax.azim = 30 ## 3d plot의 view 조절
ax.dist = 8 ## 3d plot의 view 조절
ax.elev = 5 ## 3d plot의 view 조절
ax.set_xlabel(r'$w_0$') # x축 레이블 설정
ax.set_ylabel(r'$w_1$') # y축 레이블 설정
ax.set_xticks([-5,0,5,10]) # x축 틱 간격 설정
ax.set_yticks([-5,0,5,10]) # y축 틱 간격 설정
plt.close(fig) # 자동 출력 방지
return figfig = plot_loss()
fig # loss_fn(w0hat, w1hat)을 z에 찍음
# 손실 8587.6875 를 계산하는 또 다른 방식
def l(w0hat,w1hat):
yhat = w0hat + w1hat*x
return torch.sum((y-yhat)**2)What = torch.tensor([[-5.0],[10.0]],requires_grad=True)
Whattensor([[-5.],
[10.]], requires_grad=True)
l(-5,10) # 손실 계산tensor(8587.6875)
torch.sum((y-X@What)**2) # 다른 방법tensor(8587.6875, grad_fn=<SumBackward0>)
yhat = -5 + 10*x
torch.sum((y-yhat)**2) # 다른 방법 2tensor(8587.6875)
fig
ax = fig.gca()
ax.scatter(-5, 10, l(-5,10)) # 점 찍기<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7fc9261c2bb0>
fig
fig
ax = fig.gca()
ax.scatter(-1, 3, l(-1,3)) # 다른 점 찍기<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7fc925df1eb0>
fig
- 위 과정을 반복하면 곡면을 그릴 수 있음
- 밑은 True 값 찍기
fig
ax = fig.gca()
ax.scatter(2.5, 4.0, l(2.5,4.0)) # True 값<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7fc925f14670>
fig
- 밑에 정리된 코드 과정
fig = plot_loss()
fig # loss_fn(w0hat,w1hat)
ax = fig.gca()
What = torch.tensor([[-5.0],[10.0]],requires_grad=True)
w0hat, w1hat = What.data.reshape(-1) # 언패킹
ax.scatter(w0hat, w1hat, l(w0hat, w1hat)) # x, y, z
fig # 최초의 직선에 대응하는 What 값
yhat = X@What
loss = torch.sum((y-yhat)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = None
w0hat, w1hat = What.data.reshape(-1)
ax.scatter(w0hat, w1hat, l(w0hat, w1hat))
fig # 반복 실행할수록 update됨
yhat = X@What
loss = torch.sum((y-yhat)**2)
loss.backward()
What.data = What.data - 0.001*What.grad
What.grad = None
w0hat, w1hat = What.data.reshape(-1)
ax.scatter(w0hat, w1hat, l(w0hat, w1hat), color="C1") # 앞으로는 주황색으로 색깔 고정
fig # 반복 실행할수록 점점 최소가 되는 쪽으로 진행됨
)🗣️
def plot_loss():
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
w0 = np.arange(-6, 11, 0.5)
w1 = np.arange(-6, 11, 0.5)
W1,W0 = np.meshgrid(w1,w0)
LOSS=W0*0
for i in range(len(w0)):
for j in range(len(w1)):
LOSS[i,j]=torch.sum((y-w0[i]-w1[j]*x)**2)
ax.plot_surface(W0, W1, LOSS, rstride=1, cstride=1, color='b',alpha=0.1)
ax.azim = 30 ## 3d plot의 view 조절
ax.dist = 8 ## 3d plot의 view 조절
ax.elev = 5 ## 3d plot의 view 조절
ax.set_xlabel(r'$w_0$') # x축 레이블 설정
ax.set_ylabel(r'$w_1$') # y축 레이블 설정
ax.set_xticks([-5,0,5,10]) # x축 틱 간격 설정
ax.set_yticks([-5,0,5,10]) # y축 틱 간격 설정
plt.close(fig) # 자동 출력 방지
return fig# 손실 8587.6875 를 계산하는 또 다른 방식
def l(w0hat,w1hat):
yhat = w0hat + w1hat*x
return torch.sum((y-yhat)**2)fig = plot_loss()
ax = fig.gca()
ax.scatter(2.5, 4, l(2.5,4), s=200, marker='*', color='red', label=r"${\bf W}=[2.5, 4]'$")
ax.scatter(-5, 10, l(-5,10), s=200, marker='*', color='blue', label=r"initial $\hat{\bf W}=[-5, 10]'$")
ax.legend()
fig
What = torch.tensor([[-5.0],[10.0]],requires_grad=True)
alpha = 0.001
for epoc in range(30):
yhat = X @ What
loss = torch.sum((y-yhat)**2)
loss.backward()
What.data = What.data - 0.001 * What.grad
w0,w1 = What.data.reshape(-1)
ax.scatter(w0,w1,l(w0,w1),s=5,marker='o',color='blue')
What.grad = Nonefig
- 🗣️
- B의 시각화에서 최초의 직선에 대응하는 점이 파란색 점
- 점들이 빨간색 점으로 이동하는 과정은 직선이 올라가는 과정에 대응
D. 애니메이션
from matplotlib import animationplt.rcParams['figure.figsize'] = (7.5,2.5)
plt.rcParams["animation.html"] = "jshtml" def show_animation(alpha=0.001):
## 1. 히스토리 기록을 위한 list 초기화
loss_history = []
yhat_history = []
What_history = []
## 2. 학습 + 학습과정기록
What= torch.tensor([[-5.0],[10.0]],requires_grad=True)
What_history.append(What.data.tolist())
for epoc in range(30):
yhat=X@What ; yhat_history.append(yhat.data.tolist())
loss=torch.sum((y-yhat)**2); loss_history.append(loss.item())
loss.backward()
What.data = What.data - alpha * What.grad; What_history.append(What.data.tolist())
What.grad = None
## 3. 시각화
fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
#### ax1: yhat의 관점에서..
ax1.plot(x,y,'o',label=r"$(x_i,y_i)$")
line, = ax1.plot(x,yhat_history[0],label=r"$(x_i,\hat{y}_i)$")
ax1.legend()
#### ax2: loss의 관점에서..
w0 = np.arange(-6, 11, 0.5)
w1 = np.arange(-6, 11, 0.5)
W1,W0 = np.meshgrid(w1,w0)
LOSS=W0*0
for i in range(len(w0)):
for j in range(len(w1)):
LOSS[i,j]=torch.sum((y-w0[i]-w1[j]*x)**2)
ax2.plot_surface(W0, W1, LOSS, rstride=1, cstride=1, color='b',alpha=0.1)
ax2.azim = 30 ## 3d plot의 view 조절
ax2.dist = 8 ## 3d plot의 view 조절
ax2.elev = 5 ## 3d plot의 view 조절
ax2.set_xlabel(r'$w_0$') # x축 레이블 설정
ax2.set_ylabel(r'$w_1$') # y축 레이블 설정
ax2.set_xticks([-5,0,5,10]) # x축 틱 간격 설정
ax2.set_yticks([-5,0,5,10]) # y축 틱 간격 설정
ax2.scatter(2.5, 4, l(2.5,4), s=200, marker='*', color='red', label=r"${\bf W}=[2.5, 4]'$")
ax2.scatter(-5, 10, l(-5,10), s=200, marker='*', color='blue')
ax2.legend()
def animate(epoc):
line.set_ydata(yhat_history[epoc])
ax2.scatter(np.array(What_history)[epoc,0],np.array(What_history)[epoc,1],loss_history[epoc],color='grey')
fig.suptitle(f"alpha = {alpha} / epoch = {epoc}")
return line
ani = animation.FuncAnimation(fig, animate, frames=30)
plt.close()
return ani- 🗣️ alpha:
- 학습률: update되는 폭 (ML 관점)
- step size: 오른쪽 그림 함수 관점 (산업 공학 관점)
ani = show_animation(alpha=0.001)
aniE. 학습률에 따른 시각화
- \(\alpha\)가 너무 작다면 비효율적임
show_animation(alpha=0.0001)- 🗣️ 0.001 -> 0.0001
- 아까보다 가는 둥 마는 둥 함
- \(\alpha\)가 크다고 무조건 좋은건 또 아님
show_animation(alpha=0.0083)- 🗣️ 0.001 -> 0.0083 (직접 찾은 숫자)
- 처음부터 최소점을 지나버림 (직선이 점들 위로 바로 올라감) -> 바람직하지 않음
- 이후 직선이 다시 점들 아래로 내려옴
- 왔다갔다하면서 내려오는 것 같기는 하나 효율적인 느낌은 아님
- 수틀리면 수렴안할수도??
show_animation(alpha=0.0085)- 🗣️ 0.001 -> 0.0085
- 직전의 0.0083과 얼마 차이가 나지도 않는데
- 이번에는 왔다갔다하면서 수렴하지도 않음
- 오히려 갈수록 포물선 모양으로 점점 올라감
- 그냥 망할수도??
show_animation(alpha=0.01)- 🗣️ 0.001 -> 0.01
- 기울기가 무한대가 됨
- 교훈: alpha를 잘 선택해야 수렴함
plt.rcdefaults()
plt.rcParams['figure.figsize'] = 4.5,3.0 5. SSE \(\to\) MSE 📝
🗣️(
- alpha = 0.001로 한 이유
- 갖고 있는 값이 -5, 10인데 미분값이 -1342, 1188이라 일단 1000으로 나눔
What = torch.tensor([[-5.0], [10.0]], requires_grad= True)
loss = torch.sum((y-X@What)**2)
loss.backward()
What.gradtensor([[-1342.2524],
[ 1188.9305]])
loss # 100개에 대해서 (yi - yihat)**2tensor(8587.6875, grad_fn=<SumBackward0>)
- 개수가 많아지면 loss가 커지고 그에 따라 grad도 커지므로 alpha를 비례해서 작게 만들어야 함
- 잘 선택하는 방법은 많이 연구 중 (이론적인 방법은 없음)
- alpha를 잘 못 구하는 방법
- loss에 SSE 사용 (데이터 개수가 많아지면 커짐)
- SSE보다는 데이터의 개수로 나눈 MSE를 사용
)🗣️
- 학습률을 잘 선택하는 것이 중요함
- 손실함수를 SSE로 설정하면 학습률 선택이 비효율적 \(\to\) SSE말고 MSE를 써야함
🗣️(
loss = (y-XW)'(y-XW) # What을 편의상 W로 작성
=(y'-W'X')(y-XW)
=y'y - y'XW - W'X'y + W'X'XW
loss' = -2X'y + 2X'XW
회귀분석에서는 loss'=0으로 계산
정사각행렬(2x2)인 X'X의 역행렬이 있다고 치고 곱하면
W = (X'X)^{-1}X'y
torch.linalg.inv(X.T@X)@X.T@y # What or betahat(회귀분석)tensor([[2.4459],
[4.0043]])
- 위 방법은 함수가 어려워질수록 미분이 비효율적이고 점점 구할 수 없어짐
- 그래서 컴퓨터를 이용해 알고리즘으로 구하려고 함 (경사하강법)
- 회귀분석에서는 loss’=0이나 1/100*loss’=0이나 상관없기 때문에 (양변에 100곱하면 동일)
- SSE로 푸나 MSE로 푸나 같음 -> 그냥 SSE로 계산
- 경사하강법에서는 MSE를 선택
)🗣️
손실함수가 SSE일때코드
What = torch.tensor([[-5.0],[10.0]],requires_grad = True)
for epoc in range(30):
# step1: yhat
yhat = X@What
# step2: loss
loss = torch.sum((y-yhat)**2)
# step3: 미분
loss.backward()
# step4: update
What.data = What.data - 0.001 * What.grad
What.grad = NoneWhat.datatensor([[2.4290],
[4.0144]])
손실함수가 MSE일때코드
What = torch.tensor([[-5.0],[10.0]],requires_grad = True)
for epoc in range(30):
# step1: yhat
yhat = X@What
# step2: loss
loss = torch.sum((y-yhat)**2)/100 # torch.mean((y-yhat)**2)
# step3: 미분
loss.backward()
# step4: update
What.data = What.data - 0.1 * What.grad
What.grad = NoneWhat.datatensor([[2.4290],
[4.0144]])
🗣️ loss에서 loss/100을 하면 loss’도 loss’/100이 됨 (What.grad/100) -> alpha는 100배 커져야 원래 수식과 전체 값이 동일해짐
6. 파이토치식 코딩패턴 (1) 📝
torch.manual_seed(43052)
x,_ = torch.randn(100).sort()
eps = torch.randn(100)*0.5
X = torch.stack([torch.ones(100),x],axis=1)
W = torch.tensor([[2.5],[4.0]])
y = X@W + eps.reshape(100,1)
x = X[:,[1]]A. 기본패턴
🗣️ SSE 말고 MSE로
## -- 외우세요!!! -- ##
What = torch.tensor([[-5.0],[10.0]],requires_grad = True)
for epoc in range(30):
# step1: yhat
yhat = X@What
# step2: loss
loss = torch.sum((y-yhat)**2)/100
# step3: 미분
loss.backward()
# step4: update
What.data = What.data - 0.1 * What.grad
What.grad = Noneplt.plot(x,y,'o')
plt.plot(x,X@What.data,'--')
plt.title(f'What={What.data.reshape(-1)}');
B. Step2의 수정 – loss_fn 이용
🗣️(
What = torch.tensor([[-5.0],[10.0]],requires_grad = True)yhat=X@Whattorch.sum((y-yhat)**2)/100 # MSEtensor(85.8769, grad_fn=<DivBackward0>)
torch.mean((y-yhat)**2) # MSEtensor(85.8769, grad_fn=<MeanBackward0>)
def loss_fn(yhat, y):
return torch.mean((y-yhat)**2)loss_fn(yhat,y)tensor(85.8769, grad_fn=<MeanBackward0>)
- loss_fn 원리를 잘 모른다면 pytorch 함수 이용
loss_fn = torch.nn.MSELoss()
loss_fn(yhat,y) # 결과는 동일tensor(85.8769, grad_fn=<MseLossBackward0>)
- 틀린 설명
- torch.nn.MSELoss는 함수인데, “None -> MSE를 계산해주는 함수”인 함수
- 맞는 설명
- torch.nn.MSELoss는 callable object를 생성하는 클래스
)🗣️
What = torch.tensor([[-5.0],[10.0]],requires_grad = True)
loss_fn = torch.nn.MSELoss()
for epoc in range(30):
# step1: yhat
yhat = X@What
# step2: loss
#loss = torch.sum((y-yhat)**2)/100
loss = loss_fn(yhat,y) # 여기서는 큰 상관없지만 습관적으로 yhat을 먼저넣는 연습을 하자!!
# step3: 미분
loss.backward()
# step4: update
What.data = What.data - 0.1 * What.grad
What.grad = None🗣️ loss_fn은 무조건 yhat 먼저
plt.plot(x,y,'o')
plt.plot(x,X@What.data,'--')
plt.title(f'What={What.data.reshape(-1)}');
C. Step1의 수정 – net 이용
🗣️ yhat = X@What도 알고 싶지 않다면 (네트워크 이용)
# net – net 오브젝트란?
원래 yhat을 이런식으로 구했는데 ~
What = torch.tensor([[-5.0],[10.0]],requires_grad = True)
yhat= X@What
yhat[:5]tensor([[-29.8211],
[-28.6215],
[-24.9730],
[-21.2394],
[-19.7919]], grad_fn=<SliceBackward0>)
아래와 같은 방식으로 코드를 짜고 싶음..
yhat = net(X) # 🗣️ X를 입력으로 받아 yhat을 출력하고 싶음
위와 같은 코드를 가능하게 하는 net은 torch에서 지원하고 아래와 같이 사용할 수 있음.
🗣️(
# torch.nn.Linear?net = torch.nn.Linear(
in_features= ??,
out_features= ??,
bias= False # default는 True
)
- in_features: 입력(X)에 대한 차원 (features를 dimension으로 생각)
X.shape # 100은 관측값 개수에 따라 바뀔 수 있고, 2는 모형이 정해지면 안 바뀜torch.Size([100, 2])
- out_features: 출력(y)에 대한 차원
y.shape # 마찬가지로 1torch.Size([100, 1])
net = torch.nn.Linear(
in_features= 2,
out_features= 1,
bias= False
)yhat = net(X)
yhat[:5]tensor([[-0.1600],
[-0.1362],
[-0.0639],
[ 0.0101],
[ 0.0388]], grad_fn=<SliceBackward0>)
- 원래 구한 yhat과 비교하면 What을 설정하지 않았으므로 당연히 다름
net.weight # What과 같다고 생각하면 됨Parameter containing:
tensor([[0.3320, 0.1982]], requires_grad=True)
- 엄밀히 말하면 net.weight는 2x1 matrix가 아니라 1x2 martix
- 컴퓨터 공학적 이유로 이렇게 되어 있음 (column vector보다 row vector 연산이 쉽다고 함)
net.weight.T # 이게 진짜 What과 동일tensor([[0.3320],
[0.1982]], grad_fn=<PermuteBackward0>)
net.weight.data = torch.tensor([[-5.0],[10.0]]).T
net.weight.datatensor([[-5., 10.]])
yhat= net(X)
yhat[:5]tensor([[-29.8211],
[-28.6215],
[-24.9730],
[-21.2394],
[-19.7919]], grad_fn=<SliceBackward0>)
- 원래 구한 yhat과 동일
)🗣️
# yhat = net(X)
net = torch.nn.Linear(
in_features=2, # X:(n,2) --> 2
out_features=1, # yhat:(n,1) --> 1
bias=False
)net.weight.data = torch.tensor([[-5.0], [10.0]]).T # .T 를 해야함. 외우세요
net.weightParameter containing:
tensor([[-5., 10.]], requires_grad=True)
net(X)[:5]tensor([[-29.8211],
[-28.6215],
[-24.9730],
[-21.2394],
[-19.7919]], grad_fn=<SliceBackward0>)
(X@What)[:5]tensor([[-29.8211],
[-28.6215],
[-24.9730],
[-21.2394],
[-19.7919]], grad_fn=<SliceBackward0>)
(X@net.weight.T)[:5]tensor([[-29.8211],
[-28.6215],
[-24.9730],
[-21.2394],
[-19.7919]], grad_fn=<SliceBackward0>)
#
- 수정된코드
# step1을 위한 사전준비
net = torch.nn.Linear(
in_features=2,
out_features=1,
bias=False
)
net.weight.data = torch.tensor([[-5.0, 10.0]])
# step2를 위한 사전준비
loss_fn = torch.nn.MSELoss()
for epoc in range(30):
# step1: yhat
# yhat = X@What
yhat = net(X)
# step2: loss
loss = loss_fn(yhat,y)
# step3: 미분
loss.backward()
# step4: update
net.weight.data = net.weight.data - 0.1 * net.weight.grad
net.weight.grad = None🗣️ What.data -> net.weight.data, What.grad -> net.weight.grad
plt.plot(x,y,'o')
plt.plot(x,net(X).data,'--')
plt.title(f'net.weight={net.weight.data.reshape(-1)}');
D. Step4의 수정 – optimizer의 이용
- 소망: 아래의 과정을 좀 더 편하게 했으면..
net.weight.data = net.weight.data - 0.1 * net.weight.grad
net.weight.data = None # optimizer – 이걸 이용하면 update 과정을 손쉽게 할 수 있음
기존코드
## -- 준비과정 -- ##
# step1을 위한 사전준비
net = torch.nn.Linear(
in_features=2,
out_features=1,
bias=False
)
net.weight.data = torch.tensor([[-5.0, 10.0]])
# step2를 위한 사전준비
loss_fn = torch.nn.MSELoss()## -- 1에폭진행 -- ##
# step1:
yhat = net(X)
# step2: loss
loss = loss_fn(yhat,y)
# step3: 미분
loss.backward()
# step4: update
print(net.weight.data)
net.weight.data = net.weight.data - 0.1 * net.weight.grad
print(net.weight.data)
net.weight.grad = Nonetensor([[-5., 10.]])
tensor([[-3.6577, 8.8111]])
## -- 2에폭진행 -- ##
# step1: 2에폭진행
yhat = net(X)
# step2: loss
loss = loss_fn(yhat,y)
# step3: 미분
loss.backward()
# step4: update
print(net.weight.data)
net.weight.data = net.weight.data - 0.1 * net.weight.grad
print(net.weight.data)
net.weight.grad = Nonetensor([[-3.6577, 8.8111]])
tensor([[-2.5548, 7.8612]])
새로운코드 – optimizer 이용
🗣️(
- torch.optim.SGD : optimizer를 만들어줌
- torch.optim.SGD는 net.weight를 갖고 있어야 함(What)
- net.weight는 net.parameters()로 볼 수 있음
- net.parameters()는 generator: iterable object -> list화 가능
- torch.optim.SGD는 학습률 lr도 갖고 있어야 함
net.weightParameter containing:
tensor([[-2.5548, 7.8612]], requires_grad=True)
net.parameters()<generator object Module.parameters at 0x7fc922ade820>
list(net.parameters()) # 값을 보려면[Parameter containing:
tensor([[-2.5548, 7.8612]], requires_grad=True)]
# optimizr = torch.optim.SGD(net.parameters(), lr=0.1) # net.parameters(): generator- net.weight.data = net.weight.data - 0.1 * net.weight.grad
- => optimizr.step()
- net.weight.grad = None
- => optimizr.zero_grad()
)🗣️
## -- 준비과정 -- ##
# step1을 위한 사전준비
net = torch.nn.Linear(
in_features=2,
out_features=1,
bias=False
)
net.weight.data = torch.tensor([[-5.0, 10.0]])
# step2를 위한 사전준비
loss_fn = torch.nn.MSELoss()
# step4를 위한 사전준비
optimizr = torch.optim.SGD(net.parameters(),lr=0.1)## -- 1에폭진행 -- ##
yhat = net(X)
# step2: loss
loss = loss_fn(yhat,y)
# step3: 미분
loss.backward()
# step4: update
print(net.weight.data)
#net.weight.data = net.weight.data - 0.1 * net.weight.grad
optimizr.step()
print(net.weight.data)
#net.weight.grad = None
optimizr.zero_grad()tensor([[-5., 10.]])
tensor([[-3.6577, 8.8111]])
## -- 2에폭진행 -- ##
yhat = net(X)
# step2: loss
loss = loss_fn(yhat,y)
# step3: 미분
loss.backward()
# step4: update
print(net.weight.data)
#net.weight.data = net.weight.data - 0.1 * net.weight.grad
optimizr.step()
print(net.weight.data)
#net.weight.grad = None
optimizr.zero_grad()tensor([[-3.6577, 8.8111]])
tensor([[-2.5548, 7.8612]])
#
- 수정된코드
# step1을 위한 사전준비
net = torch.nn.Linear(
in_features=2,
out_features=1,
bias=False
)
net.weight.data = torch.tensor([[-5.0, 10.0]])
# step2를 위한 사전준비
loss_fn = torch.nn.MSELoss()
# step4를 위한 사전준비
optimizr = torch.optim.SGD(net.parameters(),lr=0.1)
for epoc in range(30):
# step1: yhat
yhat = net(X)
# step2: loss
loss = loss_fn(yhat,y)
# step3: 미분
loss.backward()
# step4: update
optimizr.step()
optimizr.zero_grad()plt.plot(x,y,'o')
plt.plot(x,yhat.data,'--')
plt.title(f'net.weight={net.weight.data.reshape(-1)}');